package proxy

import (
	"encoding/json"
	"fmt"
	"log"
	"os/exec"
	"strconv"
	"sync"
	"time"

	"git.oschina.net/index.php/gostudy/util"
	"github.com/codeskyblue/go-sh"
)

type Backend struct {
	Ip     string `json:"ip"`
	Port   int    `json:"port"`
	Weight int    `json:"weight"`
}

type Proxy struct {
	Ip       string    `json:"ip"`
	Port     int       `json:"port"`
	Backends []Backend `json:"backends"`
}

type Service struct {
	sync.RWMutex
	Iface    string
	BindIp   string
	BindPort int
	Addr     string
	Store    map[string]*Proxy
}

func (s *Service) AddrPath() string {
	addr := "http://" + s.Addr + "/paasos/v1/ipservices/"
	return addr
}

// 注册服务
func (s *Service) Register() bool {
	data := map[string]string{}
	data["dip"] = s.BindIp
	data["url"] = fmt.Sprintf("http://%s:%d/v1", s.BindIp, s.BindPort)
	json_data, _ := json.Marshal(data)
	str := string(json_data)
	rest := util.HttpPost(s.AddrPath()+"register", str)
	return rest != ""
}

// 加载配置
func (s *Service) LoadConfig(vip string) {
	addr := s.AddrPath() + vip
	util.HttpGet(addr)
}

// 添加VIP
func (this *Proxy) AddVip(iface string) {
	exec.Command("sudo", "ip", "addr", "add", this.Ip, "dev", iface).Run()
}

// 删除VIP
func (this *Proxy) DelVip(iface string) {
	exec.Command("sudo", "ip", "addr", "del", this.Ip, "dev", iface).Run()
}

// 添加lvs
func (this *Proxy) AddLvs() {
	svc := fmt.Sprintf("%s:%d", this.Ip, this.Port)
	log.Println("Add lvs: ", svc)
	//ipvsadm –A –t 1.1.1.1:80 –s rr
	//	sh.Command() //.Run()
	this.DelLvs()
	exec.Command("ipvsadm", "-At", svc, "-s", "wlc").Run()
	for _, bk := range this.Backends {
		app := fmt.Sprintf("%s:%d", bk.Ip, bk.Port)
		// ipvsadm –a –t 1.1.1.1:80 –r 192.168.1.2:80 -g
		// ipvsadm -at 192.168.1.195:80 -r 192.168.1.138:31084 -g -w 4
		log.Println("=> app:" + app + ", weight:" + strconv.Itoa(bk.Weight))
		exec.Command("ipvsadm", "-at", svc, "-r", app, "-g", "-w", strconv.Itoa(bk.Weight)).Run()
	}
}

// 添加lvs
func (this *Proxy) DelLvs() {
	svc := fmt.Sprintf("%s:%d", this.Ip, this.Port)
	log.Println("Add lvs: ", svc)

	//	f, err := exec.Command("/bin/bash", "-c", "ls").Output()
	//	if err != nil {
	//		log.Println(err.Error())
	//	}
	//	log.Println(string(f))

	//	//	ipvsadm -D -t 1.1.1.1:80
	//	sh.Command("ipvsadm", "-D", "-t", svc).Run()
	//	sh.Command("echo", "hello").Output()
	exec.Command("ipvsadm", "-D", "-t", svc).Run()
}

func (this *Proxy) AddRule() {
	if len(this.Backends) == 0 {
		return
	}
	bk := this.Backends[0]
	var cmd string

	// Rule: -m comment --comment 'lvs-192.168.1.1:3000-192.168.1.1:3000'
	comment := fmt.Sprintf("-m comment --comment lvs-%s:%d-%s:%d", this.Ip, this.Port, bk.Ip, bk.Port)
	time.Sleep(time.Millisecond)

	// eg: iptables -t nat -A PREROUTING -p tcp -d 192.168.1.194 --dport 8001 -j DNAT --to 192.168.1.136:30119
	cmd = fmt.Sprintf("sudo iptables -t nat -A PREROUTING -p tcp %s -d %s --dport %d -j DNAT --to %s:%d", comment, this.Ip, this.Port, bk.Ip, bk.Port)
	util.Exec(cmd)

	// eg: iptables -t nat -A POSTROUTING -p tcp -d 192.168.1.136 --dport 30119  -j SNAT --to 192.168.1.194
	cmd = fmt.Sprintf("sudo iptables -t nat -A POSTROUTING -p tcp %s -d %s --dport %d -j SNAT --to %s", comment, bk.Ip, bk.Port, this.Ip)
	util.Exec(cmd)
}

func (this *Proxy) DelRule() {
	if len(this.Backends) == 0 {
		return
	}
	bk := this.Backends[0]
	comment := fmt.Sprintf("lvs-%s:%d-%s:%d", this.Ip, this.Port, bk.Ip, bk.Port)

	// delete dnat
	sh.Command("sudo", "iptables", "-t", "nat", "-nL", "--line-numbers").
		Command("grep", comment).
		Command("grep", "DNAT").
		Command("awk", "{print $1}").
		Command("xargs", "sudo", "iptables", "-t", "nat", "-D", "PREROUTING").Run()

	// delete snat
	sh.Command("sudo", "iptables", "-t", "nat", "-nL", "--line-numbers").
		Command("grep", comment).
		Command("grep", "SNAT").
		Command("awk", "{print $1}").
		Command("xargs", "sudo", "iptables", "-t", "nat", "-D", "POSTROUTING").Run()
}
